﻿using Chloe;
using Chloe.MySql;
using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace JW.ORM
{
    public class DbContextWrapper
    {
        private readonly IDbContextFactory _dbContextFactory;

        public DbContextWrapper(IDbContextFactory dbContextFactory)
        {
            this._dbContextFactory = dbContextFactory;
        }

        public virtual DbContext GetInternalContext<TEntity>() => _dbContextFactory.CreateDbContext(typeof(TEntity));

        public IQuery<TEntity> Query<TEntity>()
        {
            var context = GetInternalContext<TEntity>();
            return context.Query<TEntity>();
        }

        public IQuery<TEntity> Query<TEntity>(string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Query<TEntity>(table);
        }

        public TEntity QueryByKey<TEntity>(object key, bool tracking = false)
        {
            var context = GetInternalContext<TEntity>();
            return context.QueryByKey<TEntity>(key, tracking);
        }

        public TEntity QueryByKey<TEntity>(object key, string table, bool tracking = false)
        {
            var context = GetInternalContext<TEntity>();
            return context.QueryByKey<TEntity>(key, table, tracking);
        }

        public IJoiningQuery<T1, T2> JoinQuery<T1, T2>(Expression<Func<T1, T2, object[]>> joinInfo)
        {
            var context = GetInternalContext<T1>();
            return context.JoinQuery(joinInfo);
        }

        public IJoiningQuery<T1, T2, T3> JoinQuery<T1, T2, T3>(Expression<Func<T1, T2, T3, object[]>> joinInfo)
        {
            var context = GetInternalContext<T1>();
            return context.JoinQuery(joinInfo);
        }

        public IJoiningQuery<T1, T2, T3, T4> JoinQuery<T1, T2, T3, T4>(Expression<Func<T1, T2, T3, T4, object[]>> joinInfo)
        {
            var context = GetInternalContext<T1>();
            return context.JoinQuery(joinInfo);
        }

        public IJoiningQuery<T1, T2, T3, T4, T5> JoinQuery<T1, T2, T3, T4, T5>(Expression<Func<T1, T2, T3, T4, T5, object[]>> joinInfo)
        {
            var context = GetInternalContext<T1>();
            return context.JoinQuery(joinInfo);
        }

        public IEnumerable<T> SqlQuery<T>(string sql, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.SqlQuery<T>(sql, parameters);
        }

        public IEnumerable<R> SqlQuery<T, R>(string sql, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.SqlQuery<R>(sql, parameters);
        }

        public IEnumerable<T> SqlQuery<T>(string sql, CommandType cmdType, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.SqlQuery<T>(sql, cmdType, parameters);
        }

        public TEntity Insert<TEntity>(TEntity entity)
        {
            var context = GetInternalContext<TEntity>();
            return context.Insert(entity);
        }

        public Task<TEntity> InsertAsync<TEntity>(TEntity entity)
        {
            var context = GetInternalContext<TEntity>();
            return context.InsertAsync(entity);
        }

        public TEntity Insert<TEntity>(TEntity entity, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Insert(entity, table);
        }

        public object Insert<TEntity>(Expression<Func<TEntity>> content)
        {
            var context = GetInternalContext<TEntity>();
            return context.Insert(content);
        }

        public object Insert<TEntity>(Expression<Func<TEntity>> content, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Insert(content, table);
        }

        public void InsertRange<TEntity>(List<TEntity> entities, bool keepIdentity = false)
        {
            var context = GetInternalContext<TEntity>();
            context.InsertRange(entities, keepIdentity);
        }

        public int Update<TEntity>(TEntity entity)
        {
            var context = GetInternalContext<TEntity>();
            return context.Update(entity);
        }

        public int Update<TEntity>(TEntity entity, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Update(entity, table);
        }

        public int Update<TEntity>(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> content)
        {
            var context = GetInternalContext<TEntity>();
            return context.Update(condition, content);
        }

        public async Task<int> UpdateAsync<TEntity>(TEntity entity)
        {
            var context = GetInternalContext<TEntity>();
            return await context.UpdateAsync(entity);
        }

        public async Task<int> UpdateAsync<TEntity>(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> content)
        {
            var context = GetInternalContext<TEntity>();
            return await context.UpdateAsync(condition, content);
        }

        public int Update<TEntity>(Expression<Func<TEntity, bool>> condition, Expression<Func<TEntity, TEntity>> content, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Update(condition, content, table);
        }

        public int Delete<TEntity>(TEntity entity)
        {
            var context = GetInternalContext<TEntity>();
            return context.Delete(entity);
        }

        public int Delete<TEntity>(TEntity entity, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Delete(entity, table);
        }

        public int Delete<TEntity>(Expression<Func<TEntity, bool>> condition)
        {
            var context = GetInternalContext<TEntity>();
            return context.Delete(condition);
        }

        public async Task<int> DeleteAsync<TEntity>(Expression<Func<TEntity, bool>> condition)
        {
            var context = GetInternalContext<TEntity>();
            return await context.DeleteAsync(condition);
        }

        public int Delete<TEntity>(Expression<Func<TEntity, bool>> condition, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.Delete(condition, table);
        }

        public int DeleteByKey<TEntity>(object key)
        {
            var context = GetInternalContext<TEntity>();
            return context.DeleteByKey<TEntity>(key);
        }

        public int DeleteByKey<TEntity>(object key, string table)
        {
            var context = GetInternalContext<TEntity>();
            return context.DeleteByKey<TEntity>(key, table);
        }

        public void BeginTransaction<TEntity>(IsolationLevel isolationLevel = IsolationLevel.Unspecified)
        {
            var context = GetInternalContext<TEntity>();
            if (!context.Session.IsInTransaction)
            {
                context.Session.BeginTransaction(isolationLevel);
            }
        }

        public void CommitTransaction<TEntity>()
        {
            var context = GetInternalContext<TEntity>();
            if (context.Session.IsInTransaction)
            {
                context.Session.CommitTransaction();
            }
        }

        public void RollbackTransaction<TEntity>()
        {
            var context = GetInternalContext<TEntity>();
            if (context.Session.IsInTransaction)
            {
                context.Session.RollbackTransaction();
            }
        }

        public void TrackEntity<T>(object entity)
        {
            var context = GetInternalContext<T>();
            context.TrackEntity(entity);
        }

        public object ExecuteScalar<T>(string cmdText, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteScalar(cmdText, parameters);
        }

        public object ExecuteScalar<T>(string cmdText, CommandType cmdType, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteScalar(cmdText, cmdType, parameters);
        }

        public int ExecuteNonQuery<T>(string cmdText, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteNonQuery(cmdText, parameters);
        }

        public int ExecuteNonQuery<T>(string cmdText, CommandType cmdType, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteNonQuery(cmdText, cmdType, parameters);
        }

        public IDataReader ExecuteReader<T>(string cmdText, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteReader(cmdText, parameters);
        }

        public IDataReader ExecuteReader<T>(string cmdText, CommandType cmdType, params DbParam[] parameters)
        {
            var context = GetInternalContext<T>();
            return context.Session.ExecuteReader(cmdText, cmdType, parameters);
        }

        public IDbTransaction GetCurrentTransaction<T>()
        {
            var context = GetInternalContext<T>();
            return context.Session.CurrentTransaction;
        }

        public bool IsInTransaction<T>()
        {
            var context = GetInternalContext<T>();
            return context.Session.IsInTransaction;
        }

        /// <summary>利用 SqlBulkCopy 批量插入数据。</summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entities"></param>
        /// <param name="batchSize">设置 SqlBulkCopy.BatchSize 的值</param>
        /// <param name="bulkCopyTimeout">设置 SqlBulkCopy.BulkCopyTimeout 的值</param>
        /// <param name="keepIdentity">是否保留源自增值。false 由数据库分配自增值</param>
        public virtual void BulkInsert<TEntity>(List<TEntity> entities, string table = null, int? batchSize = 10000, int? bulkCopyTimeout = 60, bool keepIdentity = false)
        {
            var context = GetInternalContext<TEntity>() as MySqlContext;
            if (context == null)
            {
                throw new InvalidOperationException();
            }

            //context.BulkInsert(entities, table, batchSize, bulkCopyTimeout, keepIdentity);
        }
    }
}